package com.podmanager.data;

import android.content.ContentProvider;
import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.UriMatcher;
import android.database.Cursor;
import android.database.SQLException;
import android.database.sqlite.SQLiteConstraintException;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteQueryBuilder;
import android.net.Uri;
import android.text.TextUtils;
import android.util.Log;

public class DBContentProvider extends ContentProvider {
	// Debug tag for the LogCat
	private static final String TAG = "DBContentProvider";

	// A reference to the database wrapper class
	private PMDatabase mDB;
	
	// Construct the URI that other components will use to access this 
	// content provider. 
	private static final String AUTHORITY = "com.podmanager.data.DBContentProvider";
	private static final String PROVIDER_BASE_PATH = "podmanager";

	private static final String SUB_BASE_PATH =  PROVIDER_BASE_PATH + "/" + PMDatabase.SUBSCRIPTIONS.TABLE_NAME;
	private static final String EPI_BASE_PATH =  PROVIDER_BASE_PATH + "/" + PMDatabase.EPISODES.TABLE_NAME;
	private static final String PLAYLIST_BASE_PATH =  EPI_BASE_PATH + "/" +  PMDatabase.PLAYLIST.TABLE_NAME;
	private static final String SETTINGS_PATH = PROVIDER_BASE_PATH + "/" +PMDatabase.SETTINGS.TABLE_NAME;
	private static final String SUB_EPI_DOWNLOADED_PATH = SUB_BASE_PATH + "/" + PMDatabase.SUBSCRIPTIONS.TABLE_NAME;

	public static final Uri SUBSCRIPTION_URI = Uri.parse("content://" + AUTHORITY+"/"+ SUB_BASE_PATH);
	public static final Uri SUBSCRIPTION_SPINNER_URI = Uri.parse("content://" + AUTHORITY+"/"+ SUB_BASE_PATH+"/raw");
	//public static final Uri SUBSCRIPTIONS_ITEM_URI = Uri.parse("content://" + AUTHORITY+"/"+ SUB_BASE_PATH+"/#");
	public static final Uri EPISODES_URI = Uri.parse("content://" + AUTHORITY+"/"+ EPI_BASE_PATH);
	public static final Uri PLAYLIST_URI = Uri.parse("content://" + AUTHORITY+"/"+ PLAYLIST_BASE_PATH);
	public static final Uri SETTINGS_URI = Uri.parse("content://" + AUTHORITY +"/" + SETTINGS_PATH );
	public static final Uri SUBSCRIPTION_EPI_DOWNLOADED_URI = Uri.parse("content://" + AUTHORITY + "/" + SUB_EPI_DOWNLOADED_PATH);
	
	public static final String CONTENT_TYPE_SUBSCRIPTIONS = ContentResolver.CURSOR_DIR_BASE_TYPE 	+ "/subscriptions";
	public static final String CONTENT_ITEM_SUBSCRIPTIONS = ContentResolver.CURSOR_ITEM_BASE_TYPE 	+ "/subscriptions";
	public static final String CONTENT_TYPE_EPISODES = 		ContentResolver.CURSOR_DIR_BASE_TYPE 	+ "/episodes";
	public static final String CONTENT_ITEM_EPISODES =		ContentResolver.CURSOR_ITEM_BASE_TYPE 	+ "/episodes";
	
	//THESE ARE THE PROVIDER OPTIONS
	private static final int SUBSCRIPTIONS_ALL	= 100;
	private static final int SUBSCRIPTIONS_ITEM	= 101;
	private static final int SUBSCRIPTION_EPI_DOWNLOADS = 102;
	private static final int EPISODES_ALL		= 200;
	private static final int EPISODES_ITEM		= 201;
	private static final int PLAYLIST_ALL		= 300;
	private static final int SETTINGS 			= 400;
	private static final int SUBSCRIPTION_SPINNER			= 999;
	
	//this is the URImatcher that sets those options as things to respond to
	private static final UriMatcher sURIMatcher = new UriMatcher(UriMatcher.NO_MATCH);
	static {
		sURIMatcher.addURI(AUTHORITY, SUB_BASE_PATH			+ "/#"		, SUBSCRIPTIONS_ITEM);
		sURIMatcher.addURI(AUTHORITY, SUB_BASE_PATH						, SUBSCRIPTIONS_ALL);
		sURIMatcher.addURI(AUTHORITY, SUB_BASE_PATH			+ "/raw"	, SUBSCRIPTION_SPINNER);
		sURIMatcher.addURI(AUTHORITY, EPI_BASE_PATH						, EPISODES_ALL);
		sURIMatcher.addURI(AUTHORITY, EPI_BASE_PATH			+ "/#"		, EPISODES_ITEM);
		sURIMatcher.addURI(AUTHORITY, PLAYLIST_BASE_PATH				, PLAYLIST_ALL);
		sURIMatcher.addURI(AUTHORITY, SETTINGS_PATH						, SETTINGS);
		sURIMatcher.addURI(AUTHORITY, SUB_EPI_DOWNLOADED_PATH			, SUBSCRIPTION_EPI_DOWNLOADS);
	}

	@Override
    public boolean onCreate() {
		mDB = new PMDatabase(getContext());
		return true;
    }

    @Override
    public Uri insert(Uri uri, ContentValues values) {
		int uriType = sURIMatcher.match(uri);
		Uri returnValue = null;
		long newID=0;
		SQLiteDatabase db = mDB.getWritableDatabase();
		try {
			switch (uriType) {
			case SUBSCRIPTIONS_ALL:
				newID = db.insertOrThrow(PMDatabase.SUBSCRIPTIONS.TABLE_NAME, null, values);
		    	getContext().getContentResolver().notifyChange(DBContentProvider.SUBSCRIPTION_SPINNER_URI, null);
		    	getContext().getContentResolver().notifyChange(DBContentProvider.SUBSCRIPTION_URI, null);
				break;		
			case EPISODES_ALL:
				newID = db.insertOrThrow(PMDatabase.EPISODES.TABLE_NAME, null, values);
		    	getContext().getContentResolver().notifyChange(DBContentProvider.PLAYLIST_URI, null);
		    	getContext().getContentResolver().notifyChange(DBContentProvider.EPISODES_URI, null);
				break;
			case SETTINGS:
				newID = db.insertOrThrow(PMDatabase.SETTINGS.TABLE_NAME, null, values);
				getContext().getContentResolver().notifyChange(DBContentProvider.SETTINGS_URI, null);
				break;
			default:
				return null;			
			}
			if (newID > 0) {
				returnValue = ContentUris.withAppendedId(uri, newID);
		    	getContext().getContentResolver().notifyChange(uri, null);
			} else {
				throw new SQLException("Failed to insert row into " + uri);
			}
			
		} catch (SQLiteConstraintException e) {
	        Log.i(TAG, "Ignoring constraint failure.");
	    } catch (Exception e) {
	    	Log.i(TAG, e.getMessage());
	    }
		return returnValue;
    }
    
    @Override
    public int delete(Uri uri, String selection, String[] selectionArgs) {
		int uriType = sURIMatcher.match(uri);
		int returnValue = 0;

		/**
		 * currently only defined for actions I know we'll need
		 * delete 1 sub, delete a filtered list of episodes (when deleting a sub)
		 */
		switch(uriType) {
		case SUBSCRIPTIONS_ALL:
			returnValue = deleteSubscription(uri, selection, selectionArgs);
	    	getContext().getContentResolver().notifyChange(DBContentProvider.SUBSCRIPTION_SPINNER_URI, null);
	    	getContext().getContentResolver().notifyChange(DBContentProvider.SUBSCRIPTION_URI, null);
			break;
		case EPISODES_ALL:
			returnValue = deleteEpisodes(uri, selection, selectionArgs);
	    	getContext().getContentResolver().notifyChange(DBContentProvider.PLAYLIST_URI, null);
	    	getContext().getContentResolver().notifyChange(DBContentProvider.EPISODES_URI, null);
			break;
		case SETTINGS:
			returnValue = deleteSettings(uri, selection, selectionArgs);
			getContext().getContentResolver().notifyChange(DBContentProvider.SETTINGS_URI, null);
			break;
		case SUBSCRIPTION_EPI_DOWNLOADS:
			
			break;
		default:
			Log.e(TAG, "delete not defined for URI: " + uri.toString());
		}
    	getContext().getContentResolver().notifyChange(uri, null);
		return returnValue;
    }

    private int deleteEpisodes(Uri uri, String selection,
			String[] selectionArgs) {
    	SQLiteDatabase db = mDB.getWritableDatabase();
    	int rowsAffected = 0;
    	
    	try {	// delete all episodes that match selection
    		rowsAffected = db.delete(PMDatabase.EPISODES.TABLE_NAME, selection, selectionArgs);
    		Log.d(TAG, "in delete all episodes, rows affected = " + rowsAffected);
    		
    	} catch(Exception e) {
    		Log.e(TAG, "delete not defined for URI: " + uri.toString());
    	}
    	
		return 0;
	}
	
	private int deleteSubscription(Uri uri, String selection,
			String[] selectionArgs) {
		SQLiteDatabase db = mDB.getWritableDatabase();
		int rowsAffected = 0;
		
		try {//delete all subscriptions that match selection
			
			rowsAffected = db.delete(PMDatabase.SUBSCRIPTIONS.TABLE_NAME, 
									selection, null);
			Log.d(TAG, "in delete subscription item, rows affected = " + rowsAffected);
			
		} catch (Exception e) {
			Log.e(TAG, "delete not defined for URI: " + uri.toString());
		}
		
		return 0;
	}
	
	private int deleteSettings(Uri uri, String selection, String[] selectionArgs) {
		SQLiteDatabase db = mDB.getWritableDatabase();
		int rowsAffected = 0;
		
		try {
			rowsAffected = db.delete(PMDatabase.SETTINGS.TABLE_NAME, 
									selection, selectionArgs);
			Log.d(TAG, "in delete settings, rows affected = " + rowsAffected);
			
		} catch (Exception e) {
			Log.e(TAG, "delete not defined for URI: " + uri.toString());
		}
		
		return rowsAffected;
	}

	@Override
	public String getType(Uri uri) {
		int uriType = sURIMatcher.match(uri);
		switch (uriType) {
		case SUBSCRIPTIONS_ALL:
			return CONTENT_TYPE_SUBSCRIPTIONS;
		case SUBSCRIPTIONS_ITEM:
			return CONTENT_ITEM_SUBSCRIPTIONS;
		case EPISODES_ALL:
		case PLAYLIST_ALL:
			return CONTENT_TYPE_EPISODES;
		case EPISODES_ITEM:
			return CONTENT_ITEM_EPISODES;
		default:
			return null;			
		}
	}

    @Override
    public Cursor query(Uri uri, String[] projection, String selection,
            String[] selectionArgs, String sortOrder) {
    	Cursor returnValue = null;
		int uriType = sURIMatcher.match(uri);
		
		switch(uriType) {
		case SUBSCRIPTIONS_ALL:
			returnValue = queryAllSubscriptions(uri, projection, selection, selectionArgs, sortOrder);
			break;
		case EPISODES_ALL:
			returnValue = queryAllEpisodes(uri, projection, selection, selectionArgs, sortOrder);
			break;
		case EPISODES_ITEM:
			returnValue = queryEpisodeItem(uri, projection, selection, selectionArgs, sortOrder);
			break;
		case PLAYLIST_ALL:
			returnValue = queryPlaylistAll(uri, projection, selection, selectionArgs, sortOrder);
			break;
		case SETTINGS:
			returnValue = querySettingsAll(uri, projection, selection, selectionArgs, sortOrder);
			break;
		case SUBSCRIPTION_SPINNER:
			try {
			returnValue = queryRawSubscriptionQuery(uri, projection, selection, selectionArgs, sortOrder);
			} catch (Exception e) {
				Log.e("error", e.getMessage().toString());
			}
			break;
		default:
			Log.e(TAG, "query not defined for URI: " + uri.toString());
			return null;
		}
		return returnValue;
    }

    private Cursor queryRawSubscriptionQuery(Uri uri, String[] projection,
					String selection, String[] selectionArgs, String sortOrder) {
		SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
		String[] subQueries = new String[] {
				"SELECT _id, SUB_TITLE FROM dummy",
				"SELECT _id, SUB_TITLE FROM " + PMDatabase.SUBSCRIPTIONS.TABLE_NAME};
	
		SQLiteDatabase db = this.mDB.getWritableDatabase();
		db.execSQL("DROP TABLE IF EXISTS dummy");
	    db.execSQL("CREATE TEMP TABLE dummy (_id integer primary key autoincrement not null, SUB_TITLE text)");
	    db.execSQL("INSERT INTO dummy VALUES (0,'All Episodes')");
	    
	    
		String sql = qb.buildUnionQuery(subQueries, sortOrder, null);
		Cursor cursor = db.rawQuery(sql, null);
		cursor.setNotificationUri(getContext().getContentResolver(), DBContentProvider.SUBSCRIPTION_SPINNER_URI);
		cursor.setNotificationUri(getContext().getContentResolver(), DBContentProvider.SUBSCRIPTION_URI);

		cursor.setNotificationUri(getContext().getContentResolver(), uri);
		return cursor;
    }

	private Cursor queryAllSubscriptions(Uri uri, String[] projection,
			String selection, String[] selectionArgs, String sortOrder) {
    	//sample query code
		SQLiteQueryBuilder queryBuilder = new SQLiteQueryBuilder();
		queryBuilder.setTables(PMDatabase.SUBSCRIPTIONS.TABLE_NAME);
		SQLiteDatabase db = mDB.getReadableDatabase();
		Cursor cursor = 
				queryBuilder.query(db, projection, 
						selection, selectionArgs, null, null, sortOrder);
		cursor.setNotificationUri(getContext().getContentResolver(), DBContentProvider.SUBSCRIPTION_SPINNER_URI);
		cursor.setNotificationUri(getContext().getContentResolver(), DBContentProvider.SUBSCRIPTION_URI);

		cursor.setNotificationUri(getContext().getContentResolver(), uri);
		return cursor;
	}
    private Cursor queryAllEpisodes(Uri uri, String[] projection,
			String selection, String[] selectionArgs, String sortOrder) {
		SQLiteQueryBuilder queryBuilder = new SQLiteQueryBuilder();
		queryBuilder.setTables(PMDatabase.EPISODES.TABLE_NAME);
		SQLiteDatabase db = mDB.getReadableDatabase();
		Cursor cursor = 
				queryBuilder.query(db, projection, 
						selection, selectionArgs, null, null, sortOrder);
		cursor.setNotificationUri(getContext().getContentResolver(), DBContentProvider.EPISODES_URI);
		cursor.setNotificationUri(getContext().getContentResolver(), DBContentProvider.PLAYLIST_URI);

		cursor.setNotificationUri(getContext().getContentResolver(), uri);
		return cursor;
	}
    private Cursor queryEpisodeItem(Uri uri, String[] projection,
			String selection, String[] selectionArgs, String sortOrder) {
		SQLiteQueryBuilder queryBuilder = new SQLiteQueryBuilder();
		queryBuilder.setTables(PMDatabase.EPISODES.TABLE_NAME);
		SQLiteDatabase db = mDB.getReadableDatabase();
		Cursor cursor = 
				queryBuilder.query(db, projection, 
						selection, selectionArgs, null, null, sortOrder);
		cursor.setNotificationUri(getContext().getContentResolver(), DBContentProvider.EPISODES_URI);
		cursor.setNotificationUri(getContext().getContentResolver(), DBContentProvider.PLAYLIST_URI);

		cursor.setNotificationUri(getContext().getContentResolver(), uri);
    	return cursor;
	}
    
    private Cursor querySettingsAll(Uri uri, String[] projection,
    		String selection, String[] selectionArgs, String sortOrder) {
		SQLiteQueryBuilder queryBuilder = new SQLiteQueryBuilder();
		queryBuilder.setTables(PMDatabase.SETTINGS.TABLE_NAME);
		SQLiteDatabase db = mDB.getReadableDatabase();
		Cursor cursor =	queryBuilder.query(db, projection, 
						selection, selectionArgs, null, null, sortOrder);
		cursor.setNotificationUri(getContext().getContentResolver(), DBContentProvider.SETTINGS_URI);

		cursor.setNotificationUri(getContext().getContentResolver(), uri);
    	return cursor;
    }
    
    private Cursor queryPlaylistAll(Uri uri, String[] projection,
			String selection, String[] selectionArgs, String sortOrder) {
    	
		SQLiteQueryBuilder queryBuilder = new SQLiteQueryBuilder();
		
		queryBuilder.setTables(PMDatabase.EPISODES.TABLE_NAME + " INNER JOIN " + PMDatabase.SUBSCRIPTIONS.TABLE_NAME
								+ " ON " + (PMDatabase.EPISODES.TABLE_NAME + "." +PMDatabase.EPISODES.SUB_ROW_ID + "=" 
											+ PMDatabase.SUBSCRIPTIONS.TABLE_NAME + "." +PMDatabase.SUBSCRIPTIONS.KEY_ROW_ID));
		Cursor cursor = null;
		try {
		cursor = 
				queryBuilder.query(mDB.getReadableDatabase(), projection, 
						selection, selectionArgs, null, null, sortOrder);
		cursor.setNotificationUri(getContext().getContentResolver(), DBContentProvider.EPISODES_URI);
		cursor.setNotificationUri(getContext().getContentResolver(), DBContentProvider.PLAYLIST_URI);
		} catch (Exception e) {
			e.printStackTrace();
		}

		cursor.setNotificationUri(getContext().getContentResolver(), uri);
		return cursor;
    }
    
    public int updateEpisodeDownloadCount(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
    	int rowsAffected = 0;
    	
    	try {
	    	SQLiteDatabase db = mDB.getWritableDatabase();
			
	
			String extraWhere = uri.getLastPathSegment();
			StringBuilder modSelection = new StringBuilder();
			String table =null;
			
			table = PMDatabase.SUBSCRIPTIONS.TABLE_NAME;
			modSelection = new StringBuilder(PMDatabase.SUBSCRIPTIONS.KEY_ROW_ID + "=" + extraWhere);
			rowsAffected = db.update(table, 
					values, modSelection.toString(), null);
	    	getContext().getContentResolver().notifyChange(DBContentProvider.SUBSCRIPTION_SPINNER_URI, null);
	    	getContext().getContentResolver().notifyChange(DBContentProvider.SUBSCRIPTION_URI, null);
    	} catch (Exception e) { }
    	
    	return rowsAffected;
    }

	@Override
    public int update(Uri uri, ContentValues values, String selection,
            String[] selectionArgs) {
		int uriType = sURIMatcher.match(uri);
		SQLiteDatabase db = mDB.getWritableDatabase();
		int rowsAffected = 0;

		String extraWhere = uri.getLastPathSegment();
		StringBuilder modSelection = new StringBuilder();
		String table =null;
		
		switch (uriType) {
		case SUBSCRIPTIONS_ALL:
			table = PMDatabase.SUBSCRIPTIONS.TABLE_NAME;
			rowsAffected = db.update(table, 
					values, modSelection.toString(), null);
	    	getContext().getContentResolver().notifyChange(DBContentProvider.SUBSCRIPTION_SPINNER_URI, null);
	    	getContext().getContentResolver().notifyChange(DBContentProvider.SUBSCRIPTION_URI, null);
			break;
		case SUBSCRIPTIONS_ITEM:
			table = PMDatabase.SUBSCRIPTIONS.TABLE_NAME;
			modSelection = new StringBuilder(PMDatabase.SUBSCRIPTIONS.KEY_ROW_ID + "=" + extraWhere);
			rowsAffected = db.update(table, 
					values, modSelection.toString(), null);
	    	getContext().getContentResolver().notifyChange(DBContentProvider.SUBSCRIPTION_SPINNER_URI, null);
	    	getContext().getContentResolver().notifyChange(DBContentProvider.SUBSCRIPTION_URI, null);
			break;		
		case SUBSCRIPTION_EPI_DOWNLOADS:
			table = PMDatabase.SUBSCRIPTIONS.TABLE_NAME;
			modSelection = new StringBuilder(selection);
			rowsAffected = db.update(table, 
					values, modSelection.toString(), null);
	    	getContext().getContentResolver().notifyChange(DBContentProvider.SUBSCRIPTION_SPINNER_URI, null);
	    	getContext().getContentResolver().notifyChange(DBContentProvider.SUBSCRIPTION_URI, null);
			break;
		case EPISODES_ALL:
			table = PMDatabase.EPISODES.TABLE_NAME;
			rowsAffected = db.update(table, 
					values, selection, null);
	    	getContext().getContentResolver().notifyChange(DBContentProvider.EPISODES_URI, null);
	    	getContext().getContentResolver().notifyChange(DBContentProvider.PLAYLIST_URI, null);
			break;
		case EPISODES_ITEM:
			table = PMDatabase.EPISODES.TABLE_NAME;
			modSelection = new StringBuilder(PMDatabase.EPISODES.KEY_ROW_ID + "=" + extraWhere);
			rowsAffected = db.update(table, 
					values, modSelection.toString(), null);
	    	getContext().getContentResolver().notifyChange(DBContentProvider.EPISODES_URI, null);
	    	getContext().getContentResolver().notifyChange(DBContentProvider.PLAYLIST_URI, null);
			break;
		case SETTINGS:
			table = PMDatabase.SETTINGS.TABLE_NAME;
			// modSelection = new StringBuilder(PMDatabase.SETTINGS.KEY_ROW + "=" + extraWhere);
			rowsAffected = db.update(table,  values, selection, selectionArgs);
			getContext().getContentResolver().notifyChange(DBContentProvider.SETTINGS_URI, null);
			break;
		case PLAYLIST_ALL:
			Log.e(TAG, "update not defined for URI: " + uri.toString());
			break;
		default:
			throw new IllegalArgumentException("Unknown or Invalid URI " + uri);
		}
		if (!TextUtils.isEmpty(selection)) 
			modSelection.append(" and " + selection);

    	getContext().getContentResolver().notifyChange(uri, null);
		return rowsAffected;
    }
}
